using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Structures;


namespace X3DXamlXna
{
    public interface ICameraService
    {
        Camera CurrentCamera { get; }
        void AddViewPoint(Camera camera);
    }

    /// <summary>
    /// This is a game component that implements ICameraService_.
    /// </summary>
    public class CameraManager : Microsoft.Xna.Framework.GameComponent, ICameraService
    {

        private Camera _DefaultCamera;
        private Camera _CurrentCamera;
        private float _moovingSpeed = Consts.MOVING_SPEED;
        private float _rotatingSpeed = Consts.ROTATION_SPEED;
        private KeyboardState _old = new KeyboardState();
        private LinkedList<Camera> _viewPoints;
        private LinkedListNode<Camera> _viewPointIterator;
        private Cinematic cinematic = new Cinematic();
        


        public Camera CurrentCamera
        {
            get { return _CurrentCamera; }
            private set { _CurrentCamera = value; }
        }


        public CameraManager(Game game)
            : base(game)
        {
            game.Services.AddService(typeof(ICameraService), this);
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            //build the defaultCamera
            _viewPoints = new LinkedList<Camera>();
            _DefaultCamera = Camera.DefaultCamera;
            _DefaultCamera.AspectRatio = Game.GraphicsDevice.Viewport.AspectRatio;

            _CurrentCamera = _DefaultCamera.clone();

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            ProcessNecessaryInput();

            if (cinematic.IsActive)
            {
                cinematic.Next(gameTime);
                _CurrentCamera = cinematic.WhereIAm;
            }
            else
            {
                ProcessInput(gameTime);
            }

            base.Update(gameTime);
        }


        public void AddViewPoint(Camera camera)
        {
            _viewPoints.AddLast(camera);
            if (_viewPointIterator == null)
            {
                _viewPointIterator = _viewPoints.First;
            }
            if (_CurrentCamera.DEF.Equals("Default Camera"))
            {
                _CurrentCamera = _viewPoints.First.Value.clone();
            }
        }

        private void ProcessNecessaryInput()
        {
            var now = Keyboard.GetState();
            if (now.IsKeyDown(Keys.PageUp) && !_old.IsKeyDown(Keys.PageUp))
            {
                if (_viewPoints.Count == 0)
                {
                    cinematic.Iitialize(_CurrentCamera, _DefaultCamera);
                }
                else
                {
                    if (_viewPointIterator.Next == null)
                    {
                        _viewPointIterator = _viewPoints.First;
                    }
                    else
                    {
                        _viewPointIterator = _viewPointIterator.Next;
                    }

                    if (_viewPointIterator.Value.jump)
                    {
                        cinematic.Iitialize(_CurrentCamera, _viewPointIterator.Value);
                    }
                    else
                    {
                        _CurrentCamera = _viewPointIterator.Value.clone();
                    }
                }

            }
            else if (now.IsKeyDown(Keys.PageDown) && !_old.IsKeyDown(Keys.PageDown))
            {

                if (_viewPoints.Count == 0)
                {
                    cinematic.Iitialize(_CurrentCamera, _DefaultCamera);
                }
                else
                {
                    if (_viewPointIterator.Previous == null)
                    {
                        _viewPointIterator = _viewPoints.Last;
                    }
                    else
                    {
                        _viewPointIterator = _viewPointIterator.Previous;
                    }

                    if (_viewPointIterator.Value.jump)
                    {
                        cinematic.Iitialize(_CurrentCamera, _viewPointIterator.Value);
                    }
                    else
                    {
                        _CurrentCamera = _viewPointIterator.Value.clone();
                    }

                }


            }
            if (now.IsKeyDown(Keys.Back))
            {
                _CurrentCamera.DEF = "UserCamera";
                cinematic.Stop();

            }

            _old = now;

        }

        private void ProcessInput(GameTime gameTime)
        {
            float dt = ((float) gameTime.ElapsedGameTime.Milliseconds) / 1000.0f * _moovingSpeed;
            float dr = ((float) gameTime.ElapsedGameTime.Milliseconds) / 1000.0f * _rotatingSpeed;


            var keys = Keyboard.GetState().GetPressedKeys();

            foreach (var key in keys)
            {
                var trans = Matrix.Transpose(_CurrentCamera.View);
                switch (key)
                {
                    case Keys.W:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Forward * dt));
                        break;
                    case Keys.S:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Backward * dt));
                        break;
                    case Keys.D:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Right * dt));
                        break;
                    case Keys.A:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Left * dt));
                        break;
                    case Keys.X:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Down * dt));
                        break;
                    case Keys.Space:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.position = Vector3.Transform(_CurrentCamera.position, Matrix.CreateTranslation(trans.Up * dt));
                        break;
                    case Keys.Up:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Left, dr));
                        break;
                    case Keys.Down:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Right, dr));
                        break;
                    case Keys.LeftAlt:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Forward, dr));
                        break;
                    case Keys.LeftControl:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Backward, dr));
                        break;
                    case Keys.Right:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Down, dr));
                        break;
                    case Keys.Left:
                        _CurrentCamera.DEF = "UserCamera";
                        _CurrentCamera.orientation = Quaternion.Concatenate(_CurrentCamera.orientation, Quaternion.CreateFromAxisAngle(trans.Up, dr));
                        break;
                    case Keys.RightControl:
                        if (_moovingSpeed >= Consts.LOWER_SPEED)
                            _moovingSpeed -= Consts.SPEED_INCREMENT;
                        break;
                    case Keys.RightShift:
                        if (_moovingSpeed <= Consts.TOP_SPEED)
                            _moovingSpeed += Consts.SPEED_INCREMENT;
                        break;
                    default: break;
                }
            }


        }

    }

    class Cinematic
    {

        private float _cinematicPassed;
        private Camera _from;
        private Camera _to;
        private Camera _whereIAm;
        private bool _isActive = false;

        public Camera WhereIAm { get { return _whereIAm; } private set { } }
        public bool IsActive { get { return _isActive; } private set { } }

        public Cinematic()
        {
        }

        public void Iitialize(Camera from, Camera to)
        {
            _from  = from;
            _to = to;
            _whereIAm = _from.clone();
            _whereIAm.DEF = "Moving to " + to.DEF;
            _isActive = true;
            _cinematicPassed = 0;
        }

        public Camera Next(GameTime gameTime)
        {
            if (_cinematicPassed >= Consts.CINEMATIC_TIME)
            {
                _whereIAm = _to.clone();
                _isActive = false;
            }
            else
            {
                var dt = gameTime.ElapsedGameTime.Milliseconds / 1000f;
                _cinematicPassed += dt;

                _whereIAm.position = Vector3.SmoothStep(_from.position, _to.position, _cinematicPassed / Consts.CINEMATIC_TIME);

                _whereIAm.orientation = Quaternion.Slerp(_from.orientation, _to.orientation, _cinematicPassed / Consts.CINEMATIC_TIME);

                _whereIAm.fieldOfView = MathHelper.Lerp(_from.fieldOfView, _to.fieldOfView, _cinematicPassed / Consts.CINEMATIC_TIME);

                //whereIAm.FieldOfView = MathHelper.Lerp(fromField, toField, cinematicPassed / CINEMATIC_TIME);
                //whereIAm.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.Lerp(fromField, toField, cinematicPassed / CINEMATIC_TIME), aspectRatio, 0.1f, 2000f);

            }
            return _whereIAm;
        }

        //public bool isActive()
        //{
        //    return
        //        whereIAm.isInitialized() &&
        //        whereToGo.isInitialized() &&
        //        !whereIAm.Equals(whereToGo);
        //}

        //private void stimate()
        //{

        //    cinematicPassed = 0;

        //    fromEye = Vector3.Transform(-whereIAm.View.Translation, Matrix.Transpose(whereIAm.View));
        //    toEye = Vector3.Transform(-whereToGo.View.Translation, Matrix.Transpose(whereToGo.View));

        //    var c = whereIAm.View * Matrix.CreateTranslation(-whereIAm.View.Translation);
        //    var toRot = whereToGo.View * Matrix.CreateTranslation(-whereToGo.View.Translation);

        //    Vector3 a, b;
        //    Matrix.Transpose(c).Decompose(out a, out fromQuat, out b);
        //    Matrix.Transpose(toRot).Decompose(out a, out toQuat, out b);

        //    fromField = whereIAm.FieldOfView;
        //    toField = whereToGo.FieldOfView;

        //    whereIAm.Name = "Moving to " + whereToGo.Name;
        //}

        public void Stop()
        {
            _isActive = false;
        }

    }
}
